package com.hexinfo.dmpro.component.code.detection.service;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hex.hdtp.common.utils.AntlrUtil;
import com.hexinfo.dmpro.common.utils.StringUtils;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveLexer;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveParser;
import com.hexinfo.dmpro.component.code.detection.antlr.impala.HiveParserBaseVisitor;
import com.hexinfo.dmpro.component.code.detection.entity.ScriptPackScanResultEntity;
import com.hexinfo.dmpro.component.code.detection.enums.AntlrEnum;
import com.hexinfo.dmpro.component.code.detection.utils.SqlFormatterUtil;
import com.hexinfo.dmpro.sparing.model.ScanMetadata;
import com.hexinfo.dmpro.sparing.service.ScanMetadataService;
import lombok.extern.slf4j.Slf4j;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
@Component
public class HiveBaseSqlVisitor extends HiveParserBaseVisitor<String> {

    @Resource
    private ScanMetadataService scanMetadataService;

    public static HiveBaseSqlVisitor hiveBaseSqlVisitor;

    @PostConstruct
    public void init() {
        hiveBaseSqlVisitor = this;
        hiveBaseSqlVisitor.scanMetadataService = this.scanMetadataService;
    }

    public static final String MARK_BEGIN = "/*";
    public static final String MARK_END = "*/";
    public static final String MARK_CODE = "[CODE]";
    public static final String ERROR_CODE = "[ERROR]";
    public static final String WARN_CODE = "[WARN]";
    public static final String ERROR_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, ERROR_CODE);
    public static final String WARN_MARK_BEGIN = StrUtil.format(" {} {} {}", MARK_BEGIN, MARK_CODE, WARN_CODE);

    // 语法解析异常标识 (遇到一次解析异常之后则打标记)
    private boolean isAnalyticException = false;

    public boolean getIsAnalyticException() {
        return isAnalyticException;
    }

    // 扫描出的库表名信息
    private List<String> databaseNameList = new ArrayList<>();

    public List<String> getDatabaseNameList() {
        return databaseNameList;
    }

    // 扫描结果清单Map
    private Map<String, ScriptPackScanResultEntity> scriptPackScanResultMaps = new HashMap<>();

    // 存在时间分区字段的表
    private List<String> timePartitionTableList = new ArrayList<>();

    // 动态配置文件信息
    private Map<String, String> paramsConfig = new HashMap<>();

    // 文件名
    private String fileName = "";

    // 扫描结果清单
    public List<ScriptPackScanResultEntity> getScriptPackScanResultList() {
        return new ArrayList<>(scriptPackScanResultMaps.values());
    }

    public String visitRoot(final String sourceSql, final Map<String, ScriptPackScanResultEntity> scriptPackScanResults, List<String> timePartitionTableList, Map<String, String> paramsConfig, String fileName) {
        this.scriptPackScanResultMaps = scriptPackScanResults;
        this.timePartitionTableList = timePartitionTableList;
        this.paramsConfig = paramsConfig;
        this.fileName = fileName;
        final CodePointCharStream input = CharStreams.fromString(sourceSql);
        HiveLexer lexer = new HiveLexer(input);
        CommonTokenStream token = new CommonTokenStream(lexer);
        HiveParser parser = new HiveParser(token);
        String resultStr = this.visit(parser.statements());
        return SqlFormatterUtil.formatterSqlWithSpace(resultStr);
    }

    public String visitRoot(final String sourceSql) {
        final CodePointCharStream input = CharStreams.fromString(sourceSql);
        HiveLexer lexer = new HiveLexer(input);
        CommonTokenStream token = new CommonTokenStream(lexer);
        HiveParser parser = new HiveParser(token);
        return this.visit(parser.statements());
    }

    @Override
    public String visitCreateTableStatement(HiveParser.CreateTableStatementContext ctx) {
        String resultStr = super.visitCreateTableStatement(ctx);
        try {
            String tableName = ctx.tableName(0).getText();
            // 是否禁止出现CREATE检查
            resultStr = scanCreate(tableName, resultStr);
            // 检查建表名是否符合规范
            resultStr = scanTableName(tableName, resultStr);
            // 检查表是否已在元数据库内
            resultStr = scanTableExist(tableName, "CREATE", resultStr);
            // 检查库名是否参数化
            resultStr = scanDbName(tableName, "CREATE", resultStr);
        } catch (Exception e) {
            log.error("【antlr处理异常】visitCreateTableStatement出现异常: {}", e.getMessage(), e);
            throw new RuntimeException("【antlr处理异常】visitCreateTableStatement出现异常" + e.getMessage(), e);
        }
        return resultStr;
    }

    @Override
    public String visitColumnNameTypeOrConstraintList(HiveParser.ColumnNameTypeOrConstraintListContext ctx) {
        String resultStr = super.visitColumnNameTypeOrConstraintList(ctx);
        String allColumnInfo = AntlrUtil.assembleChildren(ctx);
        // 检查必须字段
        resultStr = scanColumnNeed(allColumnInfo, resultStr);
        return resultStr;
    }

    @Override
    public String visitColumnNameTypeConstraint(HiveParser.ColumnNameTypeConstraintContext ctx) {
        String resultStr = super.visitColumnNameTypeConstraint(ctx);
        if (AntlrUtil.queryDesignateParent(ctx, HiveParser.CreateTableStatementContext.class)) {
            // 检验字段是否都有注释
            resultStr = scanColumnComment(resultStr, ctx);
            // 检验类型是否都为String与decimal两种
            resultStr = scanColumnType(resultStr, ctx);
        }
        return resultStr;
    }

    @Override
    public String visitAlterStatement(HiveParser.AlterStatementContext ctx) {
        String resultStr = super.visitAlterStatement(ctx);
        //TODO 功能与扫描做成多个visitor分离
        // 用于统计数据库名称
        databaseNameList.add(ctx.tableName().getText());
        // 检查库名参数化
        resultStr = scanDbName(ctx.tableName().getText(), "ALTER", resultStr);
        // 是否禁止出现ALTER检查
        resultStr = scanAlter(ctx.tableName().getText(), resultStr);
        return resultStr;
    }

    @Override
    public String visitWithClause(HiveParser.WithClauseContext ctx) {
        String resultStr = super.visitWithClause(ctx);
        resultStr = withAsScan(resultStr, ctx);
        return resultStr;
    }

    @Override
    public String visitCteStatement(HiveParser.CteStatementContext ctx) {
        ctx.identifier().getText();
        ctx.KW_AS();
        return super.visitCteStatement(ctx);
    }

    @Override
    public String visitSelectClause(HiveParser.SelectClauseContext ctx) {
        String resultStr = super.visitSelectClause(ctx);
        return resultStr;
    }

    @Override
    public String visitSelectList(HiveParser.SelectListContext ctx) {
        return super.visitSelectList(ctx);
    }

    /**
     * @Method visitSubQueryExpression
     * @Param
     * @param ctx
     * @Return java.lang.String
     * @Description 子查询检查
     * @Author Wang zhihao
     * @Date 2024/2/5 16:25
     * @Version V1.0
     */
    @Override
    public String visitSubQueryExpression(HiveParser.SubQueryExpressionContext ctx) {
        String resultStr = super.visitSubQueryExpression(ctx);
        // 子查询检查
        resultStr = subqueryScan(resultStr);
        return resultStr;
    }

    @Override
    public String visitSelectItem(HiveParser.SelectItemContext ctx) {
        String resultStr = super.visitSelectItem(ctx);
        try {
            // 检查字段血缘
            resultStr = scanColumnConsanguinity(resultStr, ctx);
        } catch (Exception e) {
            log.error("【antlr处理异常】visitAtomSelectStatement出现异常: {}", e.getMessage(), e);
            throw new RuntimeException("【antlr处理异常】visitSelectItem出现异常" + e.getMessage(), e);
        }
        return resultStr;
    }

    @Override
    public String visitFunction(HiveParser.FunctionContext ctx) {
        String resultStr = super.visitFunction(ctx);
        String functionInfo = ctx.getText();
        resultStr = sumFunctionScan(functionInfo, resultStr);
        return resultStr;
    }

    @Override
    public String visitErrorNode(ErrorNode node) {
        // 错误标记只打一次(因为语法错误会导致后续行多处解析异常)
        if (!isAnalyticException) {
            isAnalyticException = true;
            String resultStr = node.getText();
            return getMarkResult(AntlrEnum.ERROR_SQL.getDesc(), "necessary") + resultStr;
        } else {
            return node.getText();
        }
    }

    @Override
    public String visitStatements(HiveParser.StatementsContext ctx) {
        return super.visitStatements(ctx);
    }

    @Override
    public String visitCompute_stats_incremental_impala(HiveParser.Compute_stats_incremental_impalaContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.COMPUTE_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.COMPUTE_CHECK.getValue(), "COMPUTE", "default", true, "warning");
        }
        return super.visitCompute_stats_incremental_impala(ctx);
    }

    @Override
    public String visitRefresh_impala(HiveParser.Refresh_impalaContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.REFRESH_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.REFRESH_CHECK.getValue(), "REFRESH", "default", true, "necessary");
        }
        return super.visitRefresh_impala(ctx);
    }

    @Override
    public String visitInsertClause(HiveParser.InsertClauseContext ctx) {
        String resultStr = super.visitInsertClause(ctx);
        // 用于统计数据库名称
        String databaseName = ctx.destination() != null
                ? ctx.destination().tableOrPartition().tableName().getText()
                : ctx.tableOrPartition().tableName().getText();
        databaseNameList.add(databaseName);
        // 库名参数化校验
        resultStr = scanDbName(databaseName, "INSERT", resultStr);
        try {
            if (scriptPackScanResultMaps.containsKey(AntlrEnum.VALUE_DB_NAME.getValue())) {
                visitChildren(ctx);
                return ctx.tableOrPartition().getText() + "#" + ctx.columnNameList().getText();
            }
            // 插入语句时分区校验(无法在分区字节点处理-可能存在实际有分区，但是插入SQL中未设置)
            resultStr = scanPartitionByMetadata(resultStr, ctx);
        } catch (Exception e) {
            log.error("【antlr处理异常】visitInsertClause出现异常: {}", e.getMessage(), e);
            throw new RuntimeException("【antlr处理异常】visitInsertClause出现异常" + e.getMessage(), e);
        }
        return resultStr;
    }

    @Override
    public String visitValuesClause(HiveParser.ValuesClauseContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.VALUE_DB_NAME.getValue())) {
            visitChildren(ctx);
            return ctx.getText();
        } else {
            return super.visitValuesClause(ctx);
        }
    }

    @Override
    public String visitRegularBody(HiveParser.RegularBodyContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.VALUE_DB_NAME.getValue())) {
            // 扫描检查insert-value语句
            String[] insertInfo = visitInsertClause(ctx.insertClause()).split("#");
            String valueInfo = visitValuesClause(ctx.valuesClause());
            scanInsert(insertInfo[0], insertInfo[1] , valueInfo);
        }
        return super.visitRegularBody(ctx);
    }

    @Override
    public String visitCommitStatement(HiveParser.CommitStatementContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.COMMIT_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.COMMIT_CHECK.getValue(), "COMMIT", "default", true, "necessary");
        }
        return super.visitCommitStatement(ctx);
    }

    @Override
    public String visitDeleteStatement(HiveParser.DeleteStatementContext ctx) {
        databaseNameList.add(ctx.tableName().getText());
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.DELETE_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.DELETE_CHECK.getValue(), "DELETE", ctx.tableName().getText(), false, "necessary");
        }
        return super.visitDeleteStatement(ctx);
    }

    @Override
    public String visitUpdateStatement(HiveParser.UpdateStatementContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.VALUE_DB_NAME.getValue())) {
            scanUpdate(ctx.tableName().getText(), ctx.setColumnsClause().getText());
        }
        return super.visitUpdateStatement(ctx);
    }

    @Override
    public String visitDropTableStatement(HiveParser.DropTableStatementContext ctx) {
        String resultStr = super.visitDropTableStatement(ctx);
        databaseNameList.add(ctx.tableName().getText());
        resultStr = scanDbName(ctx.tableName().getText(), "DROP", resultStr);
        return resultStr;
    }

    @Override
    public String visitTruncateTableStatement(HiveParser.TruncateTableStatementContext ctx) {
        String resultStr = super.visitTruncateTableStatement(ctx);
        databaseNameList.add(ctx.tablePartitionPrefix().tableName().getText());
        return resultStr;
    }

    @Override
    public String visitTablePartition(HiveParser.TablePartitionContext ctx) {
        String resultStr = super.visitTablePartition(ctx);
        // 初始化脚本建表时判断是否存在指定分区字段
        resultStr = scanPartition(resultStr, ctx);
        return resultStr;
    }

    @Override
    public String visitTableAllColumns(HiveParser.TableAllColumnsContext ctx) {
        String resultStr = super.visitTableAllColumns(ctx);
        // 校验SELECT语句中是否存在select * 语句
        resultStr = scanSelectAll(resultStr);
        return resultStr;
    }

    /**
     * @Method visitTerminal
     * @Param
     * @param node
     * @Return java.lang.String
     * @Description 重写基础节点，原基础节点返回值默认为null
     * @Author Wang zhihao
     * @Date 2024/2/1 19:23
     * @Version V1.0
     */
    @Override
    public String visitTerminal(TerminalNode node) {
        if (!node.getText().equals("<EOF>")) {
            String resultStr = node.getText();
            // 校验SELECT语句中是否存在DISTINCT关键字
            resultStr = scanDistinct(resultStr, node);
            return resultStr;
        }
        return super.visitTerminal(node);
    }

    /**
     * @Method scanPartitionByMetadata
     * @Param
     * @param resultStr
     * @param ctx
     * @Return void
     * @Description 根据元数据信息判断是否在insert时指定分区
     * @Author Wang zhihao
     * @Date 2023/11/3 18:54
     * @Version V1.0
     */
    private String scanPartitionByMetadata(String resultStr, HiveParser.InsertClauseContext ctx) {
        // 库表名全名
        String tableFullName = ctx.destination() != null
                ? ctx.destination().tableOrPartition().tableName().getText()
                : ctx.tableOrPartition().tableName().getText();
        //insert语句信息
        String insertInfo = ctx.getText();
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.SCAN_PART_BY_METADATA.getValue())) {
            String[] tableNameSplit = tableFullName.split("\\.");
            // 若有指定库名且为指定参数的，做参数替换
            String tableName = tableNameSplit.length == 1 ? tableNameSplit[0] : unFormatTableName(tableNameSplit[0]) + "." + tableNameSplit[1];
            boolean isNeedHavePart = timePartitionTableList.contains(tableName);
            boolean isHavePart = StrUtil.containsAnyIgnoreCase(insertInfo, "PARTITION");
            boolean isMark = isNeedHavePart != isHavePart;
            setScriptPackScanResults(AntlrEnum.SCAN_PART_BY_METADATA.getValue(), "insert", tableName, !isMark, "warning");
            return isMark ? getMarkResult(AntlrEnum.SCAN_PART_BY_METADATA.getDesc(), "warning") + resultStr : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method unFormatTableName
     * @Param
     * @param originalName
     * @Return java.lang.String
     * @Description 获取配置文件信息做参数替换
     * @Author Wang zhihao
     * @Date 2023/12/5 20:08
     * @Version V1.0
     */
    private String unFormatTableName(String originalName) {
        try {
            return this.paramsConfig.get(originalName.toLowerCase());
        } catch (Exception e) {
            log.error("获取JSON配置信息异常");
            throw new RuntimeException("获取JSON配置信息异常");
        }
    }

    /**
     * @Method sumFunctionScan
     * @Param
     * @param functionInfo
     * @param resultStr
     * @Return java.lang.String
     * @Description 检查sum函数规则
     * @Author Wang zhihao
     * @Date 2024/2/2 14:25
     * @Version V1.0
     */
    private String sumFunctionScan(String functionInfo, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.SUM_CHECK.getValue())) {
            boolean isMark = StrUtil.startWithIgnoreCase(functionInfo, "sum(") && !StrUtil.startWithAny(functionInfo.toLowerCase(), "sum(nvl(", "sum(coalesce(");
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.SUM_CHECK.getValue(), "default", "tableName", false, "necessary");
            }
            return isMark ? resultStr + getMarkResult(AntlrEnum.SUM_CHECK.getDesc(), "necessary") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanUpdate
     * @Param
     * @param dbTableName
     * @param setColumnClause
     * @Return void
     * @Description 扫描update语句
     * @Author Wang zhihao
     * @Date 2023/11/13 10:48
     * @Version V1.0
     */
    private void scanUpdate(String dbTableName, String setColumnClause) {
        String columnClause = StrUtil.strip(setColumnClause, "set").trim();
        List<String> columnList = Arrays.stream(columnClause.split(",")).map(String::trim).collect(Collectors.toList());
        Map<String, String> columnMap = columnList.stream().map(s -> s.split("=")).collect(Collectors.toMap(s -> s[0], s -> s[1]));
        String tableName = dbTableName.contains(".") ? dbTableName.split("\\.")[1] : dbTableName;
        matchScan(tableName, columnMap);
    }

    /**
     * @Method scanCreate
     * @Param
     * @param tableName
     * @Return void
     * @Description 是否禁止出现CREATE检查
     * @Author Wang zhihao
     * @Date 2023/10/7 12:34
     * @Version V1.0
     */
    private String scanCreate(String tableName, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.CREATE_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.CREATE_CHECK.getValue(), "CREATE", tableName, false, "necessary");
            return getMarkResult(AntlrEnum.CREATE_CHECK.getDesc(), "necessary") + "\n" + resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanCreate
     * @Param
     * @param tableName
     * @Return void
     * @Description 是否禁止出现ALTER检查
     * @Author Wang zhihao
     * @Date 2023/10/7 12:34
     * @Version V1.0
     */
    private String scanAlter(String tableName, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.ALTER_CHECK.getValue()) && !fileName.endsWith("_alter.sql")) {
            setScriptPackScanResults(AntlrEnum.ALTER_CHECK.getValue(), "ALTER", tableName, false, "necessary");
            return getMarkResult(AntlrEnum.ALTER_CHECK.getDesc(), "necessary") + "\n" + resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanTableName
     * @Param
     * @param tableName
     * @Return void
     * @Description 检查建表名是否符合规范
     * @Author Wang zhihao
     * @Date 2023/10/7 12:36
     * @Version V1.0
     */
    private String scanTableName(String tableName, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.CREATE_TABLE_NAME.getValue())) {
            boolean isMark = !checkNamesRegular(tableName);
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.CREATE_TABLE_NAME.getValue(), "CREATE", tableName, false, "necessary");
            }
            return isMark ? getMarkResult(AntlrEnum.CREATE_TABLE_NAME.getDesc(), "necessary")  + "\n" + resultStr : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanTableExist
     * @Param
     * @param tableName
     * @param sqlType
     * @Return void
     * @Description 检查表是否已在元数据库内
     * @Author Wang zhihao
     * @Date 2023/10/7 13:46
     * @Version V1.0
     */
    private String scanTableExist(String tableName, String sqlType, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.TABLE_EXISTS.getValue())) {
            LambdaQueryWrapper<ScanMetadata> wrapper = Wrappers.lambdaQuery();
            // 库名都是占位符
            String tbName = tableName.contains(".") ? tableName.split("\\.")[1] : tableName;
            wrapper.eq(ScanMetadata::getTblName, tbName);
            List<ScanMetadata> scanMetadata = hiveBaseSqlVisitor.scanMetadataService.wrapperScanMetadata(wrapper);
            boolean isMark = !scanMetadata.isEmpty();
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.TABLE_EXISTS.getValue(), sqlType, tableName, false, "warning");
            }
            return isMark ? getMarkResult(AntlrEnum.TABLE_EXISTS.getDesc(), "warning") + "\n" + resultStr : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanDbName
     * @Param
     * @param tableName
     * @param sqlType
     * @Return void
     * @Description 检查库名是否参数化
     * @Author Wang zhihao
     * @Date 2023/10/7 13:58
     * @Version V1.0
     */
    private String scanDbName(String tableName, String sqlType, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.DB_NAME_CHECK.getValue())) {
            String dbName = tableName.split("\\.")[0];
            boolean isMark = !StrUtil.startWithIgnoreCase(dbName,"${") || !StrUtil.endWithIgnoreCase(dbName, "}");
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.DB_NAME_CHECK.getValue(), sqlType, tableName, false, "necessary");
            }
            return isMark ? getMarkResult(AntlrEnum.DB_NAME_CHECK.getDesc(), "necessary") + "\n" + resultStr : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanColumnComment
     * @Param
     * @param resultStr
     * @param ctx
     * @Return java.lang.String
     * @Description 注释存在性检查
     * @Author Wang zhihao
     * @Date 2024/2/4 17:31
     * @Version V1.0
     */
    private String scanColumnComment(String resultStr, HiveParser.ColumnNameTypeConstraintContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.COLUMN_COMMENT.getValue())) {
            // 若不存在comment及具体注释则添加异常标记
            boolean isMark = ctx.KW_COMMENT() == null || ctx.StringLiteral() == null;
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.COLUMN_COMMENT.getValue(), "CREATE", "default", false, "necessary");
            }
            return isMark ? resultStr + getMarkResult(AntlrEnum.COLUMN_COMMENT.getDesc(), "necessary") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanColumnType
     * @Param
     * @param resultStr
     * @param ctx
     * @Return java.lang.String
     * @Description 检验类型是否都为String与decimal两种
     * @Author Wang zhihao
     * @Date 2024/2/4 18:36
     * @Version V1.0
     */
    private String scanColumnType(String resultStr, HiveParser.ColumnNameTypeConstraintContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.COLUMN_TYPE.getValue())) {
            // 若字段类型不为 string或decimal中的任一个则添加异常标记
            boolean isMark = ctx.colType() == null || !StrUtil.containsAnyIgnoreCase(ctx.colType().getText(), AntlrEnum.STRING.getValue(), AntlrEnum.DECIMAL.getValue());
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.COLUMN_TYPE.getValue(), "CREATE", "default", false, "warning");
            }
            return isMark ? resultStr + getMarkResult(AntlrEnum.COLUMN_TYPE.getDesc(), "warning") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanColumnNeed
     * @Param
     * @param fieldColumnList
     * @param resultStr
     * @Return java.lang.String
     * @Description 扫描必须字段
     * @Author Wang zhihao
     * @Date 2024/2/4 18:19
     * @Version V1.0
     */
    private String scanColumnNeed(String fieldColumnList, String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.COLUMN_NEED.getValue())) {
            boolean insertTime = StrUtil.containsAnyIgnoreCase(fieldColumnList, "inserttime string");
            boolean batchNo = StrUtil.containsAnyIgnoreCase(fieldColumnList, "batchno decimal");
            setScriptPackScanResults(AntlrEnum.COLUMN_NEED.getValue(), "CREATE", "default", insertTime && batchNo, "warning");
            boolean isMark = !insertTime || !batchNo;
            return isMark ? resultStr + getMarkResult(AntlrEnum.COLUMN_NEED.getDesc(), "warning") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanColumnConsanguinity
     * @Param
     * @Return void
     * @Description 检查字段血缘
     * @Author Wang zhihao
     * @Date 2023/10/27 15:08
     * @Version V1.0
     */
    private String scanColumnConsanguinity(String resultStr, HiveParser.SelectItemContext ctx) {
        // 是否开启扫描，且检查该字段是否为库表字段
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.FILED_CONSANGUINITY.getValue())
                && AntlrUtil.queryDesignateChildren(ctx, HiveParser.TableOrColumnContext.class)) {
            boolean isMark = !StrUtil.startWith(ctx.getText(), "$") && !StrUtil.contains(ctx.getText(), '.');
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.FILED_CONSANGUINITY.getValue(), "SELECT", "default", false, "warning");
            }
            return isMark ? resultStr + getMarkResult(AntlrEnum.FILED_CONSANGUINITY.getDesc(), "warning") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method subqueryScan
     * @Param
     * @param resultStr
     * @Return java.lang.String
     * @Description 子查询检查
     * @Author Wang zhihao
     * @Date 2024/2/5 17:06
     * @Version V1.0
     */
    private String subqueryScan(String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.SUB_QUERY_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.SUB_QUERY_CHECK.getValue(), "SELECT", "default", false, scriptPackScanResultMaps.get(AntlrEnum.SUB_QUERY_CHECK.getValue()).getScanLevel());
            return resultStr + getMarkResult(AntlrEnum.SUB_QUERY_CHECK.getDesc(), scriptPackScanResultMaps.get(AntlrEnum.SUB_QUERY_CHECK.getValue()).getScanLevel());
        }
        return resultStr;
    }

    /**
     * @Method scanPartition
     * @Param
     * @param resultStr
     * @param ctx
     * @Return java.lang.String
     * @Description 初始化建表语句中是否存在指定分区字段
     * @Author Wang zhihao
     * @Date 2024/2/5 10:58
     * @Version V1.0
     */
    private String scanPartition(String resultStr, HiveParser.TablePartitionContext ctx) {
        // 前提：父节点为建表语句，且分区字段开启检查
        if (AntlrUtil.queryDesignateParent(ctx, HiveParser.CreateTableStatementContext.class)
                && scriptPackScanResultMaps.containsKey(AntlrEnum.PARTITION_CHECK.getValue())) {
            // 获取分区字段信息
            String partitionInfo = AntlrUtil.assembleChildren(ctx);
            boolean isMark = !StrUtil.containsIgnoreCase(partitionInfo, AntlrEnum.SPECIFY_PARTITION_FIELD.getValue());
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.PARTITION_CHECK.getValue(), "CREATE", "default", false, "warning");
            }
            return isMark ? resultStr + getMarkResult(AntlrEnum.PARTITION_CHECK.getDesc(), "warning") : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method setScriptPackScanResults
     * @Param
     * @param scanName
     * @param sqlType
     * @param scanTable
     * @param scanState
     * @Return void
     * @Description 配置返回参数
     * @Author Wang zhihao
     * @Date 2023/10/7 10:43
     * @Version V1.0
     */
    private void setScriptPackScanResults(String scanName, String sqlType, String scanTable, boolean scanState, String level) {
        scriptPackScanResultMaps.get(scanName).setSqlType(sqlType);
        scriptPackScanResultMaps.get(scanName).setScanTable(scanTable);
        scriptPackScanResultMaps.get(scanName).setScanState(scanState ? "success" : (level.equalsIgnoreCase("necessary") ? "fail" : "warning"));
    }

    /**
     * @Method checkNamesRegular
     * @Param
     * @param name
     * @Return boolean
     * @Description 检查表名是否符合规范
     * @Author Wang zhihao
     * @Date 2023/10/7 11:02
     * @Version V1.0
     */
    private boolean checkNamesRegular(String name) {
        String tableName = name.contains(".") ? name.split("\\.")[1] : name;
        return tableName.matches(AntlrEnum.FILE_NAME_REGULAR.getValue());
    }

    /**
     * @Method scanDistinct
     * @Param
     * @param resultStr
     * @param node
     * @Return void
     * @Description 禁止使用distinct检查
     * @Author Wang zhihao
     * @Date 2023/10/7 16:25
     * @Version V1.0
     */
    private String scanDistinct(String resultStr, TerminalNode node) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.DISTINCT_CHECK.getValue())
                && StrUtil.equalsIgnoreCase(node.getText(), AntlrEnum.DISTINCT.getValue())
                && node.getParent().getClass() == HiveParser.SelectClauseContext.class) {
            setScriptPackScanResults(AntlrEnum.DISTINCT_CHECK.getValue(), "SELECT", "default", false, "warning");
            return resultStr + getMarkResult(AntlrEnum.DISTINCT_CHECK.getDesc(), "warning");
        }
        return resultStr;
    }

    /**
     * @Method scanSelectAll
     * @Param
     * @param resultStr
     * @Return void
     * @Description 不允许select *检查
     * @Author Wang zhihao
     * @Date 2023/10/7 16:27
     * @Version V1.0
     */
    private String scanSelectAll(String resultStr) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.SELECT_ALL_CHECK.getValue())) {
            setScriptPackScanResults(AntlrEnum.SELECT_ALL_CHECK.getValue(), "SELECT", "default", false, "warning");
            return resultStr + getMarkResult(AntlrEnum.SELECT_ALL_CHECK.getDesc(), "warning");
        }
        return resultStr;
    }

    /**
     * @Method scanWithAs
     * @Param
     * @param resultStr
     * @param ctx
     * @Return void
     * @Description 禁用 with as 语句检查
     * @Author Wang zhihao
     * @Date 2023/10/7 16:35
     * @Version V1.0
     */
    private String withAsScan(String resultStr, HiveParser.WithClauseContext ctx) {
        if (scriptPackScanResultMaps.containsKey(AntlrEnum.WITH_AS_CHECK.getValue())
                && AntlrUtil.queryDesignateParent(ctx, HiveParser.QueryStatementExpressionContext.class)) {
            String withAsInfo = AntlrUtil.assembleChildren(ctx);
            boolean isMark = StrUtil.startWithIgnoreCase(withAsInfo,"with") && StrUtil.containsAnyIgnoreCase(withAsInfo, "as(", "as (");
            if (isMark) {
                setScriptPackScanResults(AntlrEnum.WITH_AS_CHECK.getValue(), "WITH AS", "default", false, "warning");
            }
            return isMark ? getMarkResult(AntlrEnum.WITH_AS_CHECK.getDesc(), "warning") + resultStr : resultStr;
        }
        return resultStr;
    }

    /**
     * @Method scanInsert
     * @Param
     * @param dbTableName
     * @param insertInfo
     * @param valueInfo
     * @Return void
     * @Description 扫描检查insert-value语句
     * @Author Wang zhihao
     * @Date 2023/10/11 14:32
     * @Version V1.0
     */
    private void scanInsert(String dbTableName, String insertInfo, String valueInfo) {
        String tableName = dbTableName.contains(".") ? dbTableName.split("\\.")[1] : dbTableName;
        List<String> insertInfos = Arrays.stream(insertInfo.split(",")).map(String::trim).collect(Collectors.toList());
        List<String> valueInfos = Arrays.stream(StringUtils.strip(valueInfo,"VALUES()").split(",")).map(String::trim).collect(Collectors.toList());
        Map<String, String> insertValueMaps = IntStream.range(0, insertInfos.size())
                .collect(HashMap::new,(m,i)->m.put(insertInfos.get(i),valueInfos.get(i)),(m,n)->{});
        if (insertInfos.size() != valueInfos.size()) {
            throw new RuntimeException("扫描检查insert-value语句：字段数量与值的数量不对应");
        }
        // 根据表名匹配扫描检查
        matchScan(tableName, insertValueMaps);
    }

    /**
     * @Method matchScan
     * @Param
     * @param tableName
     * @Return void
     * @Description 根据表名匹配扫描检查
     * @Author Wang zhihao
     * @Date 2023/10/11 16:20
     * @Version V1.0
     */
    private void matchScan(String tableName, Map<String, String> insertValueMaps) {
        switch (tableName.toUpperCase()) {
            case "CTL_LOAD_CONFIG":
                scanCtlLoad(tableName, insertValueMaps);
                break;
            case "CTL_AUDIT_PARAM":
            case "CTL_AUDIT_PARAM_NEW":
                scanCtlAudit(tableName, insertValueMaps);
                break;
            case "CTL_PARAMS_CONFIG":
                scanCtlConfig(tableName, insertValueMaps);
                break;
            default:
                break;
        }
    }

    /**
     * @Method scanCtlLoad
     * @Param
     * @Return void
     * @Description 下载文件扫描
     * @Author Wang zhihao
     * @Date 2023/10/11 16:27
     * @Version V1.0
     */
    private void scanCtlLoad(String tableName, Map<String, String> insertValueMaps) {
        checkLoadAuditColumnParametric(tableName, insertValueMaps.get("POST_SQL_IMPALA"));
        checkLoadAuditColumnParametric(tableName, insertValueMaps.get("TRG_SCHEMA"));
    }

    /**
     * @Method scanCtlAudit
     * @Param
     * @Return void
     * @Description 稽核文件扫描
     * @Author Wang zhihao
     * @Date 2023/10/11 16:28
     * @Version V1.0
     */
    private void scanCtlAudit(String tableName, Map<String, String> insertValueMaps) {
        checkLoadAuditColumnParametric(tableName, insertValueMaps.get("SRC_SQL"));
        checkLoadAuditColumnParametric(tableName, insertValueMaps.get("TRG_SQL"));
    }

    /**
     * @Method scanCtlConfig
     * @Param
     * @Return void
     * @Description 推数文件扫描
     * @Author Wang zhihao
     * @Date 2023/10/11 16:28
     * @Version V1.0
     */
    private void scanCtlConfig(String tableName, Map<String, String> insertValueMaps) {
        checkPushColumnParametric(tableName, insertValueMaps.get("C_PUSHSQL"));
        checkPushColumnParametric(tableName, insertValueMaps.get("C_INCSQL"));
        checkPushColumnParametric(tableName, insertValueMaps.get("C_TASQL"));
    }

    /**
     * @Method checkColumnParametric
     * @Param
     * @Return void
     * @Description 检验下载与稽核文件中字段是否参数化
     * @Author Wang zhihao
     * @Date 2023/10/11 18:16
     * @Version V1.0
     */
    private void checkLoadAuditColumnParametric(String tableName, String columnValue) {
        if (StrUtil.isNotBlank(columnValue) && !StrUtil.containsAnyIgnoreCase(columnValue, "$${schema_stg}", "$${schema_ods}", "$${schema_dw}")) {
            setScriptPackScanResults(AntlrEnum.VALUE_DB_NAME.getValue(), "INSERT INTO", tableName, false, "necessary");
        }
    }

    /**
     * @Method checkConfigColumnParametric
     * @Param
     * @param tableName
     * @param columnValue
     * @Return void
     * @Description 检验推数文件中字段是否参数化
     * @Author Wang zhihao
     * @Date 2023/10/11 18:39
     * @Version V1.0
     */
    private void checkPushColumnParametric(String tableName, String columnValue) {
        if (StrUtil.isNotBlank(columnValue) && !StrUtil.containsAnyIgnoreCase(columnValue, "$${schema_stg}", "$${schema_ods}", "$${schema_dw}")) {
            setScriptPackScanResults(AntlrEnum.VALUE_DB_NAME.getValue(), "INSERT INTO", tableName, false, "necessary");
        }
    }

    /**
     * @Method scanClauseSub
     * @Param
     * @param clauseInfo
     * @Return void
     * @Description 扫描SQL语句中是否存在子查询
     * @Author Wang zhihao
     * @Date 2023/10/27 17:06
     * @Version V1.0
     */
    private void scanClauseSub(String clauseInfo) {
        if (StrUtil.containsAnyIgnoreCase(clauseInfo.toUpperCase(Locale.ROOT), "SELECT")) {
            setScriptPackScanResults(AntlrEnum.SUB_QUERY_CHECK.getValue(), "SELECT", "default", false, "warning");
        }
    }

    /**
     * @Method getMarkResult
     * @Param
     * @param scanName
     * @param scanLevel
     * @Return java.lang.String
     * @Description 获取SQL中的不同等级异常标记信息
     * @Author Wang zhihao
     * @Date 2024/2/6 13:34
     * @Version V1.0
     */
    //todo 优化或许做成一个实体对象来获取
    private String getMarkResult(String scanName, String scanLevel) {
        switch (scanLevel.toUpperCase(Locale.ROOT)) {
            case "WARNING":
                return StrUtil.format("{} {} {}", WARN_MARK_BEGIN, scanName, MARK_END);
            case "NECESSARY":
                return StrUtil.format("{} {} {}", ERROR_MARK_BEGIN, scanName, MARK_END);
            default:
                return "";
        }
    }

}
